﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Xml.Serialization;

namespace TtfLibrary
{
    public class TtfSimpleGlyph: IAfterRead, IAfterWrite
    {
        [TtfStop]
        public UInt16[] EndPtsOfContours;
        public UInt16 InstructionLength;
        public byte[] Instructions;
        public GlyphFlag[] GlyphFlags;
        public int[] XCoordinates;
        public int[] YCoordinates;

        public int NumberOfContours;
        public int CoordinateCount;

        [Flags]
        public enum GlyphFlag
        {
            OnCurve = 1,
            XShortVector = 2,
            YShortVector = 4,
            Repeat = 8,
            ThisXIsSame = 16,
            ThisYIsSame = 32,
            Reserved1 = 64,
            Reserved2 = 128
        }

        public void AfterRead(TtfStructureReader reader, int startOffset)
        {
            EndPtsOfContours = new UInt16[NumberOfContours];
            for (int i = 0; i < NumberOfContours; i++)
                EndPtsOfContours[i] = reader.Read<UInt16>();

            InstructionLength = reader.Read<UInt16>();
            Instructions = reader.ReadBytes(InstructionLength);

            CoordinateCount = NumberOfContours != 0 ? EndPtsOfContours.Last() + 1 : 0;

            GlyphFlags = new GlyphFlag[CoordinateCount];
            for (int i = 0; i < CoordinateCount; i++)
            {
                var currentFlag = GlyphFlags[i] = (GlyphFlag)reader.Read<byte>();
                if (currentFlag.HasFlag(GlyphFlag.Repeat))
                {
                    var repeatCount = reader.Read<byte>();
                    while (repeatCount-- > 0)
                        GlyphFlags[++i] = currentFlag;
                }
            }

            Func<GlyphFlag, GlyphFlag, int[]> readCoordinates = (shortFlag, sameFlag) =>
            {
                var coordinates = new int[CoordinateCount];
                for (int i = 0; i < CoordinateCount; i++)
                {
                    GlyphFlag glyphFlag = GlyphFlags[i];
                    bool isShort = glyphFlag.HasFlag(shortFlag);
                    bool modifier = glyphFlag.HasFlag(sameFlag);

                    // short = 0, modifier = 0  =>  + read 16 bit
                    // short = 0, modifier = 1  =>  last coord
                    // short = 1, modifier = 0  =>  + read 8 bit
                    // short = 1, modifier = 1  =>  + read 8 bit * -1

                    coordinates[i] = (i > 0 ? coordinates[i - 1] : 0) + (isShort ?
                        (reader.Read<byte>() * (modifier ? 1 : -1)) : (modifier ? 0 : reader.Read<Int16>()));
                }
                return coordinates;
            };

            XCoordinates = readCoordinates(GlyphFlag.XShortVector, GlyphFlag.ThisXIsSame);
            YCoordinates = readCoordinates(GlyphFlag.YShortVector, GlyphFlag.ThisYIsSame);
        }

        public void AfterWrite(StructureWriter writer)
        {
            throw new NotImplementedException();
            //writer.WriteBytes(OriginalContent);
        }
    }

    public class TtfCompositeGlyphComponent: IAfterRead
    {
        [TtfStop]
        public GlyphFlagC GlyphFlagsC;
        public UInt16 GlyphIndex;

        public UInt16 Arg1;
        public UInt16 Arg2;

        public UInt16 Scale;
        public UInt16 XScale;
        public UInt16 YScale;
        public UInt16 Scale01;
        public UInt16 Scale10;

        [Flags]
        public enum GlyphFlagC
        {
            [Description("If this is set, the arguments are words; otherwise, they are bytes.")]
            Arg1And2AreWords = 1,
            [Description("If this is set, the arguments are xy values; otherwise, they are points.")]
            ArgsAreXyValues = 2,
            [Description("For the xy values if the preceding is true.")]
            RoundXyToGrid = 4,
            [Description("This indicates that there is a simple scale for the component. Otherwise, scale = 1.0.")]
            WeHaveAScale = 8,
            [Description("This bit is reserved. Set it to 0.")]
            Reserved = 16,
            [Description("Indicates at least one more glyph after this one.")]
            MoreComponents = 32,
            [Description("The x direction will use a different scale from the y direction.")]
            WeHaveAnXAndYScale = 64,
            [Description("There is a 2 by 2 transformation that will be used to scale the component.")]
            WeHaveATwoByTwo = 128,
            [Description("Following the last component are instructions for the composite character.")]
            WeHaveInstructions = 256,
            [Description("If set, this forces the aw and lsb (and rsb) for the composite to be equal to those from this original glyph. This works for hinted and unhinted characters.")]
            UseMyMetrics = 512,
            [Description("Used by Apple in GX fonts.")]
            OverlapCompound = 1024,
            [Description("Composite designed to have the component offset scaled (designed for Apple rasterizer).")]
            ScaledComponentOffset = 2048,
            [Description("Composite designed not to have the component offset scaled (designed for the Microsoft TrueType rasterizer).")]
            UnscaledComponentOffset = 4096,
        }

        public void AfterRead(TtfStructureReader reader, int startOffset)
        {
            GlyphFlagsC = (GlyphFlagC)reader.Read<UInt16>();
            GlyphIndex = reader.Read<UInt16>();

            bool argsAreWords = GlyphFlagsC.HasFlag(GlyphFlagC.Arg1And2AreWords);
            UInt16 arg1 = argsAreWords ? reader.Read<UInt16>() : reader.Read<byte>();
            UInt16 arg2 = argsAreWords ? reader.Read<UInt16>() : reader.Read<byte>();

            if (GlyphFlagsC.HasFlag(GlyphFlagC.WeHaveAScale))
                Scale = reader.Read<UInt16>();
            else if (GlyphFlagsC.HasFlag(GlyphFlagC.WeHaveAnXAndYScale))
            {
                XScale = reader.Read<UInt16>();
                YScale = reader.Read<UInt16>();
            }
            else if (GlyphFlagsC.HasFlag(GlyphFlagC.WeHaveATwoByTwo))
            {
                XScale = reader.Read<UInt16>();
                Scale01 = reader.Read<UInt16>();
                Scale10 = reader.Read<UInt16>();
                YScale = reader.Read<UInt16>();
            }
        }
    }

    public class TtfCompositeGlyph: IAfterRead
    {
        [TtfStop]
        public byte[] Instructions;

        public List<TtfCompositeGlyphComponent> Components = new List<TtfCompositeGlyphComponent>();

        public void AfterRead(TtfStructureReader reader, int startOffset)
        {
            TtfCompositeGlyphComponent component;
            //do
            //{
                component = new TtfCompositeGlyphComponent();
                Components.Add(component);
            //}
            //while (component.GlyphFlagsC.HasFlag(TtfCompositeGlyphComponent.GlyphFlagC.MoreComponents));

            /*if (component.GlyphFlagsC.HasFlag(TtfCompositeGlyphComponent.GlyphFlagC.WeHaveInstructions))
            {
                var length = reader.Read<UInt16>();
                Instructions = reader.ReadBytes(length);
            }*/
        }
    }

    public class TtfGlyfData : IAfterRead, IAfterWrite
    {
        public Int16 NumberOfContours;
        public Int16 XMin;
        public Int16 YMin;
        public Int16 XMax;
        public Int16 YMax;

        [TtfStop]
        public TtfSimpleGlyph SimpleGlyph;
        public TtfCompositeGlyph CompositeGlyph;

        public void AfterRead(TtfStructureReader reader, int startOffset)
        {
            if (NumberOfContours >= 0)
            {
                SimpleGlyph = new TtfSimpleGlyph() { NumberOfContours = NumberOfContours };
                reader.Read(SimpleGlyph); 
               
                if(SimpleGlyph.XCoordinates.Min() != XMin || SimpleGlyph.YCoordinates.Min() != YMin ||
                    SimpleGlyph.XCoordinates.Max() != XMax || SimpleGlyph.YCoordinates.Max() != YMax)
                    throw new Exception("Error");
            }
            else
            {
                throw new NotImplementedException("CompositeGlyph");
                /*CompositeGlyph = new TtfCompositeGlyph();
                reader.Read(CompositeGlyph);*/
            }
        }

        public void AfterWrite(StructureWriter writer)
        {
            if (NumberOfContours >= 0)
            {
                writer.WriteObject(SimpleGlyph);
            }
            else
            {
                throw new NotImplementedException("CompositeGlyph");
            }
        }
    }

    public class OttGlyf: IAfterWrite
    {
        [TtfStop]
        public TtfGlyfData[] Glyphs;
        public int ReadBytes = 0;

        public byte[] OriginalContent;

        public void Read(TtfStructureReader reader, int[] offsets)
        {
            reader.StartRegion();

            ReadBytes = 0;

            var length = offsets.Length;
            Glyphs = new TtfGlyfData[length];
            for (int i = 0; i < length; i++)
            {
                reader.Position = offsets[i];
                Glyphs[i] = reader.Read<TtfGlyfData>();
                ReadBytes += (reader.Position - offsets[i]);
            }

            // TODO: fix write
            OriginalContent = reader.EndRegionAndGetContent();
        }

        public void AfterWrite(StructureWriter writer)
        {
            /*for (int index = 0; index < Glyphs.Length; index++)
            {
                var item = Glyphs[index];
                writer.StartRegion();
                writer.WriteObject(item);
                writer.EndRegion();
            }*/
            writer.WriteBytes(OriginalContent);
            writer.WriteBytes(new byte[]{ 0 });
        }
    }
}
